開發網頁程式會需要很多不同的互動事件(Events)
React 是開發網頁程式的工具之一
所以勢必也可以使用這些 events
今天就來稍微介紹一下 React 中 Events 的用法啦~
React 的事件函式參數event
不是本地事件(Native Events)
而是一個叫做合成事件(SyntheticEvent)的物件
它具備跨瀏覽器(cross-browser)以及可重複使用(reusable)的特性
可以重複使用是什麼意思呢?
簡單來說
所有回調函式的event
參數(亦可簡寫e
)就像是 同一個合成事件參數
React 只使用一個事件監聽器(EventListener)
且沒有把事件處理直接和 DOM 節點連接
合成事件具有下面這些屬性
boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
boolean isDefaultPrevented()
void stopPropagation()
boolean isPropagationStopped()
DOMEventTarget target
number timeStamp
string type
在回調函式被調用之後event
中的這些屬性都將會失效
因此可以被重複使用
這麼做可以節省記憶體
提升網站效能
除此之外
SyntheticEvent 的這個event
參數
若是切換到其他的執行緒
也會找不到原先具備的那些屬性
下面有一段程式碼:
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
clickEvent:"",
eventType:""
}
this.onClick = this.onClick.bind(this);
};
onClick(event) {
console.log(event); // => nullified object.
console.log(event.type); // => "click"
const eventType = event.type; // => "click"
setTimeout(function() {
console.log(event.type); // => null
console.log(eventType); // => "click"
}, 0);
// Won't work. this.state.clickEvent will only contain null values.
this.setState({clickEvent: event});
// You can still export event properties.
this.setState({eventType: event.type});
}
render() {
return (
<div>
<button onClick = {this.onClick}>Click</button>
</div>
);
}
}
export default App;
透過setTimeout
函式在另一條執行緒上試著讓他紀錄event
的屬性資料
可以看到event
一旦換到不同的執行緒
他的屬性資料event.type
就會被清除變成null
只有透過變數eventType
額外儲存的屬性資料才被保留下來
如果出現需要保留這些屬性資料的情況
可以在回調函式中呼叫event.persist()
透過event.persist()
就可以讓合成事件(synthetic event)脫離事件池(pool)
並且讓使用者的程式碼可以對其進行存取
這是官方文件中的一段程式碼
執行結果:
這段程式碼說明了合成事件重複使用的特性
也可以在onClick
這個回調函式中呼叫event.persist()
執行看看
會發現原本消失的資料又重新回到眼前了
SyntheticEvent 是 React 在事件處理上很不一樣的一個地方
除了上面事件參數是 React 特有的合成參數以外
事件處理還有幾個比較不一樣的地方
下面我們用這段程式碼來說明:
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0,
}
this.updateState = this.updateState.bind(this);
};
updateState(event) {
var score = this.state.value;
score += 1;
this.setState({value: score});
}
render() {
return (
<div>
<button onClick = {this.updateState}>Add</button>
<h4>{this.state.value}</h4>
</div>
);
}
}
export default App;
在我們習慣的網頁程式中
我們會對 DOM 物件加上監聽器(EventListener)來讓網頁元素產生反應
但是在 React 我們不用對每個網頁元素這麼做
因為 React 採用單一事件處理器
所以可以在render
的時候直接加入事件處理:
render() {
return (
<div>
<button onClick = {this.updateState}>Add</button>
</div>
);
}
寫法上和 html 很像
但是有一些些的不一樣
在 html 上的寫法是這樣:
<button onclick = "updateState()">Add</button>
但是在 React 的寫法則是:
<button onClick = {this.updateState}>Add</button>
React 的事件(onclick)要使用駝峰式寫法來表示而不是小寫字母
事件觸發也不是直接給它事件函式
取而代之的是給他一個函式名稱的字串
在 html 我們可以透過return false
來讓事件失效
<button onclick = "console.log("clicked"); return false">Add</button>
但是在 React 這樣的寫法是無效的
想要讓事件不被觸發
要用這樣的寫法:
updateState(event) {
event.preventDefault(); // 讓事件無效化
var score = this.state.value;
score += 1;
this.setState({value: score});
}
官方文件表示 React 要在對應的回調函式中呼叫event.preventDefault()
這裡的event
也是上面所提過的合成事件
不過這個函式貌似目前有些問題(?
實際使用我發現它並不能真的阻止事件觸發
我去四處追尋答案並自己實驗後
updateState(event) {
// event.preventDefault(); // 無效
// event.stopPropagation(); // 無效
// e.nativeEvent.stopImmediatePropagation(); // 無效
// return false; // 有效
var score = this.state.value;
score += 1;
this.setState({value: score});
}
上面的寫法都有人說
但是裡面只有return false
成功讓事件不被觸發
甚至有人回報這是 React 的 bug
不過
通常網頁開發要阻止事件觸發的方式有非常多種
也可以透過標籤屬性(tag attribute)或樣式表(style list)來達到目的
這邊就讓我暫時掛一個問號吧!
在 React 中
回調函式有一個必須要注意的地方
那就是 函式綁定
React 中會用到非常多的this
其中函式回調(callback)時
若是沒有事先綁定好
JSX 回調的this
就會變成未定義
導致錯誤而沒有辦法正常運作
綁定的寫法是這樣:
constructor(props) {
super(props);
this.state = {
value: 0,
}
// 綁定函式寫法
this.updateState = this.updateState.bind(this);
};
如果無論如何都不想綁定這個函式
可以在呼叫函式的地方使用箭頭函數(=>)
這個函數可以綁定this
變數
解決this
未定義的情況
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
}
};
updateState(e) {
var score = this.state.value;
score += 1;
this.setState({value: score});
}
render() {
return (
<div>
<button onClick = {(e) => this.updateState(e)}>Add</button>
<h4>{this.state.value}</h4>
</div>
);
}
}
export default App;
還有一個官方提供的解決辦法是
updateState = () => {
var score = this.state.value;
score += 1;
this.setState({value: score});
}
...
render() {
return (
<div>
<button onClick = {this.updateState}>Add</button>
<h4>{this.state.value}</h4>
</div>
);
}
不過官方表示這是個試驗性的寫法
具備使用實驗性的public class fields syntax
條件
才可以透過這個辦法解決回調this
未定義的問題
不得不說事件這裡可能是第一個碰到比較難以理解的部分
不僅用法不同
想從根本理解所以嘗試追朔原因
有些地方還是會讓人無法釐清
不過至少透過實際寫code並且執行學會了怎麼使用
歡迎大家提出不一樣的看法來討論 =D
- Eva Vue.js 30天隨身包
- Ben那些年,我們一起錯過的Javascript
- Ray激戰ReactJS 30天
Day11 end
by 瑞Ray (๑´ㅂ`๑)
event.stopPropagation()應該是阻止接下來有其他handler去繼續使用這個event
f(e) {
e.stopPropagation();// 不加這個的話,點擊b2就會執行2次 f()
console.log(e.currentTarget);
}
<div onClick={this.f.bind(this)}>
b1
<button onClick={this.f.bind(this)}>b2</button>
</div>
我之前也遇過一個需要使用event.preventDefault()的情況
https://stackoverflow.com/questions/50230048/react-ondrop-is-not-firing
喔喔喔 我明白了
因為不是終止執行,而是避免重複執行,所以才會有失效的感覺。
// PS. 剛剛查了一下函式名稱 Propogation 是「傳播」的意思,「停止他的傳遞」換句話說就是確保唯一,英文不是那麼好 =P。
長知識了 感謝大大分享!
ㄟ只是分享一下經驗值,我也是javascript菜雞1隻 :)
OK 菜雞互相扶持走向大大之路XD